package com.xm.rsi.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xm.rsi.annotation.AuthCheck;
import com.xm.rsi.annotation.RateLimit;
import com.xm.rsi.common.BaseResponse;
import com.xm.rsi.common.DeleteRequest;
import com.xm.rsi.common.ErrorCode;
import com.xm.rsi.common.ResultUtils;
import com.xm.rsi.constant.UserConstant;
import com.xm.rsi.exception.BusinessException;
import com.xm.rsi.model.dto.audit.AuditAddRequest;
import com.xm.rsi.model.dto.filedata.FileDataAddRequest;
import com.xm.rsi.model.dto.filedata.FileDataMapQueryRequest;
import com.xm.rsi.model.dto.filedata.FileDataQueryRequest;
import com.xm.rsi.model.dto.filedata.FileDataUpdateRequest;
import com.xm.rsi.model.entity.Audit;
import com.xm.rsi.model.entity.FileData;
import com.xm.rsi.model.entity.User;
import com.xm.rsi.model.vo.FileDataCountVO;
import com.xm.rsi.model.vo.FileDataVO;
import com.xm.rsi.service.AuditService;
import com.xm.rsi.service.FileDataService;
import com.xm.rsi.service.UserService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.xm.rsi.utils.EmailUtils.randomCode;
import static com.xm.rsi.utils.FileReader.*;

@RestController
@RequestMapping("/fileData")
@Slf4j
public class FileDataController {

    @Resource
    private FileDataService fileDataService;

    @Resource
    private UserService userService;

    @Resource
    private AuditService auditService;

    private static Map<Long, InputStream> inputStreamMap = new HashMap<>();
    private static Map<Long, String> inputFilename = new HashMap<>();

    /**
     * 上传文件
     *
     * @param file
     */
    @PostMapping("/uploadFile")
    public BaseResponse<String> uploadFile(MultipartFile file, Long userId) {
        try {
            if (userId == null || userId <= 0) {
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户登录过期，请重新登录");
            }
            inputStreamMap.put(userId, file.getInputStream());
            inputFilename.put(userId, file.getOriginalFilename());
            log.info("上传文件成功");
            return ResultUtils.success("ok");
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "解析文件失败");
        }
    }
    
    @PostMapping("/uploadDirectory")
    @AuthCheck(mustRole = UserConstant.ROOT_ROLE)
    @RateLimit
    @Transactional
    public BaseResponse<Integer> uploadDirectory(MultipartFile file, Long userId, HttpServletRequest request) {
        if (userId == null || userId <= 0) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户登录过期，请重新登录");
        }
        User loginUser = userService.getLoginUser(request);
        if (!loginUser.getId().equals(userId)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "用户不一致，无权操作");
        }
        String filePath = file.getOriginalFilename();
        String userDir = System.getProperty("user.dir");
        String destinationFolder = userDir + File.separator + userId + "_" + randomCode() + "_" + filePath;
        String temp = userDir + File.separator + userId + "_" + randomCode() + "_" + filePath ;
        File tempFile = new File(temp);
        File folder = null;
        try (InputStream inputStream = file.getInputStream();
             FileOutputStream outputStream = new FileOutputStream(tempFile)) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            tempFile.delete();
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "解压文件失败");
        }
        String type = isCompressedFile(filePath);
        if (type != null) {
            try {
                switch (type) {
                    case ".zip":
                        unzip(tempFile, destinationFolder.split("\\.zip")[0]);
                        folder = new File(destinationFolder.split("\\.zip")[0]);
                        break;
                    case ".rar":
                        unRar(tempFile, destinationFolder.split("\\.rar")[0]);
                        folder = new File(destinationFolder.split("\\.rar")[0]);
                        break;
                    case ".tar.gz":
                        unTarGz(tempFile, destinationFolder.split("\\.tar\\.gz")[0]);
                        folder = new File(destinationFolder.split("\\.tar\\.gz")[0]);
                        break;
                    case ".7z":
                        un7z(tempFile, destinationFolder.split("\\.7z")[0]);
                        folder = new File(destinationFolder.split("\\.7z")[0]);
                        break;
                }
                try {
                    List<FileData> fileDataList = new ArrayList<>();
                    try {
                        fileDataList = fileDataService.uploadDirectory(folder, userId);
                    } catch (BusinessException e) {
                        fileDataService.delFileDataList(fileDataList);
                        throw new BusinessException(e.getCode(), e.getMessage());
                    } catch (Exception e) {
                        fileDataService.delFileDataList(fileDataList);
                        throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
                    }
                    deleteFile(folder, tempFile);
                    return ResultUtils.success(fileDataList.size());
                } catch (BusinessException e) {
                    deleteFile(folder, tempFile);
                    throw new BusinessException(e.getCode(), e.getMessage());
                } catch (Exception e) {
                    deleteFile(folder, tempFile);
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "上传文件异常");
                }
            } catch (Exception e) {
                deleteFile(folder, tempFile);
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "解压文件失败");
            }
        } else {
            tempFile.delete();
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "上传文件不是压缩包");
        }
    }

    /**
     * 添加影像数据
     *
     * @param fileDataAddRequest
     * @param request
     * @return
     */
    @SneakyThrows
    @PostMapping("/add")
    public BaseResponse<Long> fileDataAdd(@RequestBody FileDataAddRequest fileDataAddRequest, HttpServletRequest request) {
        if (fileDataAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Double upLongitude = fileDataAddRequest.getUpLongitude();
        Double downLongitude = fileDataAddRequest.getDownLongitude();
        Double leftLatitude = fileDataAddRequest.getLeftLatitude();
        Double rightLatitude = fileDataAddRequest.getRightLatitude();
        Integer spatialResolution = fileDataAddRequest.getSpatialResolution();
        Integer radiationResolution = fileDataAddRequest.getRadiationResolution();
        String sensor = fileDataAddRequest.getSensor();
        String projection = fileDataAddRequest.getProjection();
        Integer cloudCover = fileDataAddRequest.getCloudCover();
        Date getTime = fileDataAddRequest.getGetTime();
        User loginUser = userService.getLoginUser(request);
        Long loginUserId = loginUser.getId();
        if (!inputStreamMap.containsKey(loginUserId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请上传文件");
        }
        InputStream fileInputStream = inputStreamMap.get(loginUserId);
        String filename = inputFilename.get(loginUserId);
        if (StringUtils.isAnyBlank(sensor, projection) || ObjectUtils.anyNull(upLongitude, downLongitude, leftLatitude, rightLatitude, spatialResolution, radiationResolution, cloudCover, getTime, fileInputStream)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请检查表单数据");
        }
        if (upLongitude < downLongitude || rightLatitude < leftLatitude) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请填写正确的经纬度");
        }
        Long res = fileDataService.addOne(fileDataAddRequest, fileInputStream, filename, request);
        if (res != null) {
            fileInputStream.close();
            inputStreamMap.remove(loginUserId).close();
            inputFilename.remove(loginUserId);
        }
        return ResultUtils.success(res);
    }

    /**
     * 修改
     *
     * @param fileDataUpdateRequest
     * @param request
     * @return
     */
    @SneakyThrows
    @PostMapping("/update")
    public BaseResponse<Boolean> updateFileData(@RequestBody FileDataUpdateRequest fileDataUpdateRequest, HttpServletRequest request) {
        Double upLongitude = fileDataUpdateRequest.getUpLongitude();
        Double downLongitude = fileDataUpdateRequest.getDownLongitude();
        Double leftLatitude = fileDataUpdateRequest.getLeftLatitude();
        Double rightLatitude = fileDataUpdateRequest.getRightLatitude();
        Integer spatialResolution = fileDataUpdateRequest.getSpatialResolution();
        Integer radiationResolution = fileDataUpdateRequest.getRadiationResolution();
        String sensor = fileDataUpdateRequest.getSensor();
        String projection = fileDataUpdateRequest.getProjection();
        Integer cloudCover = fileDataUpdateRequest.getCloudCover();
        Date getTime = fileDataUpdateRequest.getGetTime();
        User loginUser = userService.getLoginUser(request);
        Long loginUserId = loginUser.getId();
        if (StringUtils.isAnyBlank(sensor, projection) || ObjectUtils.anyNull(upLongitude, downLongitude, leftLatitude, rightLatitude, spatialResolution, radiationResolution, cloudCover, getTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请检查表单数据");
        }
        if (upLongitude < downLongitude || rightLatitude < leftLatitude) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请填写正确的经纬度");
        }
        Boolean res;
        if (!inputStreamMap.containsKey(loginUserId)) {
            res = fileDataService.updOne(fileDataUpdateRequest, null, "", request);
        } else {
            InputStream fileInputStream = inputStreamMap.get(loginUserId);
            String filename = inputFilename.get(loginUserId);
            res = fileDataService.updOne(fileDataUpdateRequest, fileInputStream, filename, request);
            if (res != null && res) {
                fileInputStream.close();
                inputStreamMap.remove(loginUserId).close();
                inputFilename.remove(loginUserId);
            }
        }
        return ResultUtils.success(res);
    }

    /**
     * 删除
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> delFileData(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        FileData fileData = fileDataService.getById(deleteRequest);
        if (fileData == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        User user = userService.getLoginUser(request);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        }
        Long userId = fileData.getCreateUser();
        if (fileDataService.isOwn(userId, request)) {
            fileDataService.delFile(fileData.getUrl());
            return ResultUtils.success(fileDataService.removeById(fileData));
        }
        throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "该文件不是你创建的，无权删除");
    }
    
    @PostMapping("/deleteList")
    @AuthCheck(anyRole = {UserConstant.ADMIN_ROLE, UserConstant.ROOT_ROLE})
    @Transactional
    public BaseResponse<Integer> delList(@RequestParam List<Long> idList) {
        int count = 0;
        for (Long id : idList) {
            FileData fileData = fileDataService.getById(id);
            if (fileData == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "数据" + id + "不存在");
            }
            boolean b = fileDataService.removeById(id);
            if (b) {
                fileDataService.delFile(fileData.getUrl());
                count++;
            }
        }
        return ResultUtils.success(count);
    }

    /**
     * 获取所有
     *
     * @param fileDataQueryRequest
     * @return
     */
    @PostMapping("/listFileData")
    @RateLimit
    @AuthCheck(anyRole = {UserConstant.ADMIN_ROLE, UserConstant.ROOT_ROLE})
    public BaseResponse<Page<FileData>> listFileData(@RequestBody FileDataQueryRequest fileDataQueryRequest) {
        long current = fileDataQueryRequest.getCurrent();
        long pageSize = fileDataQueryRequest.getPageSize();
        if (pageSize > 50) {
            return null;
        }
        Page<FileData> fileDataPage = fileDataService.getPage(new Page<>(current, pageSize), fileDataService.getQueryWrapper(fileDataQueryRequest));
        return ResultUtils.success(fileDataPage);
    }

    /**
     * 获取单条数据
     *
     * @param id
     * @return
     */
    @PostMapping("/getOne")
    @RateLimit
    public BaseResponse<FileData> getOne(Long id) {
        return ResultUtils.success(fileDataService.getById(id));
    }

    /**
     * 获取所有（脱敏）
     *
     * @param fileDataQueryRequest
     * @return
     */
    @PostMapping("/listFileDataVO")
    @RateLimit
    public BaseResponse<Page<FileDataVO>> listFileDataVO(@RequestBody FileDataQueryRequest fileDataQueryRequest) {
        long current = fileDataQueryRequest.getCurrent();
        long pageSize = fileDataQueryRequest.getPageSize();
        if (pageSize > 50) {
            return null;
        }
        fileDataQueryRequest.setFileDataState(0);
        Page<FileData> fileDataPage = fileDataService.getPage(new Page<>(current, pageSize), fileDataService.getQueryWrapper(fileDataQueryRequest));
        return ResultUtils.success(fileDataService.getFileDataPageVo(fileDataPage));
    }

    /**
     * 获取用户自己所有
     *
     * @param fileDataQueryRequest
     * @return
     */
    @PostMapping("/listFileDataOwn")
    @RateLimit
    public BaseResponse<Page<FileData>> listFileDataOwn(@RequestBody FileDataQueryRequest fileDataQueryRequest, HttpServletRequest request) {
        long current = fileDataQueryRequest.getCurrent();
        long pageSize = fileDataQueryRequest.getPageSize();
        if (pageSize > 50) {
            return null;
        }
        User user = userService.getLoginUser(request);
        QueryWrapper<FileData> queryWrapper = fileDataService.getQueryWrapper(fileDataQueryRequest);
        queryWrapper.eq("createUser", user.getId());
        Page<FileData> fileDataPage = fileDataService.page(new Page<>(current, pageSize), queryWrapper);
        return ResultUtils.success(fileDataPage);
    }

    /**
     * 审核
     *
     * @param auditAddRequest
     * @return
     */
    @PostMapping("/audit")
    public BaseResponse<Boolean> auditOne(@RequestBody AuditAddRequest auditAddRequest, HttpServletRequest request) {
        if (auditAddRequest == null || auditAddRequest.getFileDataId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        return ResultUtils.success(fileDataService.audit(auditAddRequest, request));
    }

    /**
     * 获得审核结果
     *
     * @param fileDataId
     * @return
     */
    @PostMapping("/getRes")
    @RateLimit
    public BaseResponse<Audit> getRes(Long fileDataId) {
        QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("fileDataId", fileDataId);
        queryWrapper.eq("auditRes", -1);
        queryWrapper.orderByDesc("createTime");
        queryWrapper.last("limit 1");
        return ResultUtils.success(auditService.getOne(queryWrapper));
    }

    /**
     * 我的审核
     * @param current
     * @param pageSize
     * @param request
     * @return
     */
    @PostMapping("/listMyAudit")
    @RateLimit
    @AuthCheck(anyRole = {UserConstant.ADMIN_ROLE, UserConstant.ROOT_ROLE})
    public BaseResponse<Page<FileData>> listMyAudit(Integer current, Long pageSize, HttpServletRequest request) {
        if (pageSize > 50) {
            return null;
        }
        User loginUser = userService.getLoginUser(request);
        QueryWrapper<Audit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUser.getId());
        List<Audit> list = auditService.list(queryWrapper);
        List<FileData> fileDataList = new ArrayList<>();
        list.forEach(audit -> {
            QueryWrapper<FileData> fileDataQueryWrapper = new QueryWrapper<>();
            fileDataQueryWrapper.eq("id", audit.getFileDataId());
            List<FileData> dataList = fileDataService.list(fileDataQueryWrapper);
            fileDataList.addAll(dataList);
        });
        List<FileData> distinctFileDataList = fileDataList.stream()
                .distinct()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(FileData::getId))),
                        ArrayList::new));
        Page<FileData> fileDataPage = new Page<>(current, pageSize, distinctFileDataList.size());
        fileDataPage.setRecords(distinctFileDataList);
        return ResultUtils.success(fileDataPage);
    }

    /**
     * 统计
     * @return
     */
    @PostMapping("/count")
    @AuthCheck(anyRole = {UserConstant.ADMIN_ROLE, UserConstant.ROOT_ROLE})
    @RateLimit
    public BaseResponse<FileDataCountVO> countAll() {
        return ResultUtils.success(fileDataService.countAll());
    }

    /**
     * 点面查询
     * @param fileDataMapQueryRequest
     * @return
     */
    @PostMapping("/pointSer")
    @RateLimit
    public BaseResponse<Page<FileDataVO>> pointSer(@RequestBody FileDataMapQueryRequest fileDataMapQueryRequest) {
        if (fileDataMapQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Map<String,String> map = (Map<String, String>) JSON.parse(fileDataMapQueryRequest.getMapStr());
        if (map == null || map.isEmpty()) {
            return ResultUtils.success(null);
        }
        return ResultUtils.success(fileDataService.AllSer(fileDataMapQueryRequest.getCurrent(), fileDataMapQueryRequest.getPageSize(), map));
    }
}
